
Screenshot: Giving It a Shot
文件:
•. t11.rb
•. cannon.rb
•. lcdrange.rb
在这个示例里,我们加入一个定时器,用于实现打炮的动画。
CannonField现在有了开炮的功能。
include Math
我们包含了Math,因为我们需要sin()和cos()函数。
@timerCount = 0
@autoShootTimer = Qt::Timer.new(self)
connect(@autoShootTimer, SIGNAL('timeout()'),
self, SLOT('moveShot()'))
@shootAngle = 0
@shootForce = 0
我们初始化那些新的私有变量,再将Qt::Timer::timeout()信号连接到moveShot()信号槽上。每当定时器超时的时候,我们就会移动子弹。
timerCount用于记录自从打炮以来所经过的时间。在打炮时,shootAngle将会是加农炮的角度,shootForce将会是加农炮的力道。
def shoot()
if @autoShootTimer.isActive()
return
end;
@timerCount = 0
@shootAngle = @currentAngle
@shootForce = @currentForce
@autoShootTimer.start(5)
end
除非已经有一颗子弹在空中了,不然的话,这个函数就会发出一发炮弹。timerCount会被重置为0. shootAngle和shootForce变量会被设置成加农炮当前的角度和力道。最后,我们启动定时器。
def moveShot()
region = Qt::Region.new(shotRect())
@timerCount += 1
shotR = shotRect()
if shotR.x() > width() || shotR.y() > height()
@autoShootTimer.stop()
else
region = region.unite(Qt::Region.new(shotR))
end
update(region)
end
moveShot()是用来移动子弹的信号槽,每隔5毫秒,当 Qt::Timer发射信号时就会执行一次。
它的任务就是:计算出新的位置;更新屏幕,让子弹显示在新的位置;并且在必要的时候停止定时器。
首先,我们创建一个记录有旧的子弹区域(shotRect())的Qt::Region。一个Qt::Region可用于保护任意类型的区域,这里我们用它来简化绘图过程。shotRect()返回子弹当前所在的位置的矩形区域。我们稍后会详细说明这一点。
然后我们增加timerCount,所产生的效果就是子弹沿着弹道向前移动一步。
下一步就是计算出新的子弹矩形区域。
如果子弹已经超出了本部件的右边缘或底部边缘的话,我们就停止定时器,否则我们将新的子弹区域(shotRect())加到Qt::Region上去。
最后,我们重绘Qt::Region。这会引起单个的绘图(paint)事件,在其中会对一个或两个需要更新的矩形进行更新。
def paintEvent(event)
painter = Qt::Painter.new(self)
paintCannon(painter)
if @autoShootTimer.isActive()
paintShot(painter)
end
painter.end()
end
绘图事件函数相对于前一章做了简化。大部分的逻辑代码都移动到了新的paintShot()和paintCannon()函数中。
def paintShot(painter)
painter.setPen(Qt::NoPen)
painter.setBrush(Qt::Brush.new(Qt::black))
painter.drawRect(shotRect())
end
这个私有函数会绘制一个以黑色填充的矩形,以此来绘制出子弹。
我们这里略过paintCannon()的实现代码说明;它与前一章中的Qt::Widget::paintEvent()代码相同。
def shotRect()
gravity = 4.0
time = @timerCount / 20.0
velocity = @shootForce
radians = @shootAngle * 3.14159265 / 180.0
velx = velocity * cos(radians)
vely = velocity * sin(radians)
x0 = (@barrelRect.right() + 5.0) * cos(radians)
y0 = (@barrelRect.right() + 5.0) * sin(radians)
x = x0 + velx * time
y = y0 + vely * time - 0.5 * gravity * time * time
result = Qt::Rect.new(0, 0, 6, 6)
result.moveCenter(Qt::Point.new(x.round, height() - 1 - y.round))
return result
end
这个私有函数计算子弹的中心点,并且返回包含着子弹的矩形。它在计算过程中会用到最初的开炮力道和角度,以及timerCount,最后这个参数会随时时间而增加。
所使用的是在无摩擦力的重力场中物体位移的标准牛顿力学公式。出于简单性考虑,我们在此忽略了任何的爱因斯坦力学效应。
我们在一个y坐标向上增长的坐标系里计算中心点。计算完了中心点之后,我们构造出一个尺寸为 6 x 6 的Qt::Rect,然后将它的中心点移动到刚才计算到的位置处。在同一个操作中,我们将那个点的坐标换算回到本部件自身的坐标系统中(参考坐标系统)。
唯一的区别就是加入了Shoot按钮。
shoot = Qt::PushButton.new(tr('&Shoot'))
shoot.setFont(Qt::Font.new('Times', 18, Qt::Font::Bold))
在构造函数中,我们就像之前的Quit 按钮一样,创建并且设置好Shoot 按钮。
connect(shoot, SIGNAL('clicked()'), cannonField, SLOT('shoot()'))
将Shoot 按钮的clicked()信号连接到CannonField 的shoot()信号槽。
加农炮现在可以打炮了,但是现在还没有射击目标。
将子弹变成一个实心的圆形。[提示:可能会用上Qt::Painter::drawEllipse()。]
当有子弹在空中飞时,将加农炮变成另一种颜色。
陈海茵
HxLauncher: Launch Android applications by voice commands